package cn.lili.modules.wallet.serviceimpl;


import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.event.TransactionCommitSendMQEvent;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.SnowFlake;
import cn.lili.common.vo.PageVO;
import cn.lili.exchange.AmqpExchangeProperties;
import cn.lili.modules.payment.entity.dos.UserWithdrawApply;
import cn.lili.modules.payment.entity.dto.UserWalletUpdateDTO;
import cn.lili.modules.payment.entity.dto.UserWithdrawalMessage;
import cn.lili.modules.payment.entity.enums.PlatformWalletEnum;
import cn.lili.modules.payment.entity.enums.WalletServiceTypeEnum;
import cn.lili.modules.payment.entity.enums.WithdrawStatusEnum;
import cn.lili.modules.payment.entity.enums.WithdrawalModeEnum;
import cn.lili.modules.payment.entity.vo.WalletVO;
import cn.lili.modules.payment.entity.vo.WithdrawApplyQueryVO;
import cn.lili.modules.system.client.SettingClient;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.WithdrawalSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.wallet.mapper.WithdrawApplyMapper;
import cn.lili.modules.wallet.service.WalletService;
import cn.lili.modules.wallet.service.WithdrawApplyService;
import cn.lili.mybatis.util.PageUtil;
import cn.lili.routing.UserRoutingKey;
import cn.lili.util.AmqpMessage;
import cn.lili.util.AmqpSender;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RedissonClient;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Objects;


/**
 * 会员提现申请业务层实现
 *
 * @author pikachu
 * @since 2020-02-25 14:10:16
 */
@Service
@RequiredArgsConstructor
public class WithdrawApplyServiceImpl extends ServiceImpl<WithdrawApplyMapper, UserWithdrawApply> implements WithdrawApplyService {

    /**
     * 会员余额
     */
    private final WalletService walletService;

    private final AmqpSender amqpSender;

    private final AmqpExchangeProperties amqpExchangeProperties;

    /**
     * 设置
     */
    private final SettingClient settingClient;


    private final ApplicationEventPublisher applicationEventPublisher;


    private final RedissonClient redisson;

    /**
     * 提现方法
     * 1、先执行平台逻辑，平台逻辑成功后扣减第三方余额，顺序问题为了防止第三方提现成功，平台逻辑失败导致第三方零钱已提现，而我们商城余额未扣减
     * 2、如果余额扣减失败 则抛出异常，事务回滚
     *
     * @param price 提现金额
     * @return
     */
    @Override
    @Transactional
    public Boolean applyWithdrawal(Double price) {

        if (price == null || price <= 0) {
            throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_AMOUNT_ERROR);
        }

        UserWithdrawalMessage userWithdrawalMessage = new UserWithdrawalMessage();
        AuthUser authUser = Objects.requireNonNull(UserContext.getCurrentUser());
        //构建审核参数
        UserWithdrawApply userWithdrawApply = new UserWithdrawApply();
        userWithdrawApply.setUserId(UserContext.getCurrentId());
        userWithdrawApply.setUsername(authUser.getUsername());
        userWithdrawApply.setApplyMoney(price);
        userWithdrawApply.setApplyStatus(WithdrawStatusEnum.APPLY.name());
        userWithdrawApply.setSn("W" + SnowFlake.getId());
        //校验该次提现是否需要审核,如果未进行配置 默认是需要审核
        Setting setting = settingClient.get(SettingEnum.WITHDRAWAL_SETTING.name());
        if (setting != null) {
            //如果不需要审核则审核自动通过
            WithdrawalSetting withdrawalSetting = new Gson().fromJson(setting.getSettingValue(),
                    WithdrawalSetting.class);
            if (Boolean.FALSE.equals(withdrawalSetting.getApply())) {
                userWithdrawApply.setApplyStatus(WithdrawStatusEnum.VIA_AUDITING.name());
                userWithdrawApply.setInspectRemark("系统自动审核通过");
                //校验金额是否满足提现，因为是从余额扣减，所以校验的是余额
                WalletVO walletVO = walletService.getWalletVO(userWithdrawApply.getUserId());
                if (walletVO.getBalance() < price) {
                    throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT);
                }
                userWithdrawalMessage.setStatus(WithdrawStatusEnum.VIA_AUDITING.name());
                //微信零钱提现
                Boolean result = withdrawal(userWithdrawApply);
                if (Boolean.TRUE.equals(result)) {
                    walletService.withdrawal(UserWalletUpdateDTO.builder()
                            .userId(userWithdrawApply.getUserId())
                            .scene(authUser.getScene())
                            .detail("余额提现")
                            .nickname(authUser.getNickName())
                            .amount(price)
                            .serviceType(WalletServiceTypeEnum.WITHDRAWAL).build());
                }
            } else {
                userWithdrawalMessage.setStatus(WithdrawStatusEnum.APPLY.name());

                //扣减余额到冻结金额
                walletService.reduceWithdrawal(UserWalletUpdateDTO.builder()
                        .userId(userWithdrawApply.getUserId())
                        .scene(authUser.getScene())
                        .detail("提现金额已冻结，审核成功后到账")
                        .nickname(authUser.getNickName())
                        .amount(price)
                        .serviceType(WalletServiceTypeEnum.WITHDRAWAL).build());
            }
            //发送余额提现申请消息

            userWithdrawalMessage.setUserId(userWithdrawApply.getUserId());
            userWithdrawalMessage.setPrice(price);
            userWithdrawalMessage.setDestination(WithdrawalModeEnum.WECHAT);
            applicationEventPublisher.publishEvent(
                    TransactionCommitSendMQEvent.builder()
                            .source("USER_WITHDRAWAL")
                            .exchange(amqpExchangeProperties.getUser())
                            .routingKey(UserRoutingKey.USER_WITHDRAWAL)
                            .message(userWithdrawalMessage)
                            .build()
            );
        }
        return this.save(userWithdrawApply);

    }

    @Override
    public Boolean withdrawal(UserWithdrawApply userWithdrawApply) {
        userWithdrawApply.setInspectTime(new Date());
        //保存或者修改零钱提现
        this.saveOrUpdate(userWithdrawApply);
        //TODO 调用自动提现接口
        boolean result = true;
        //如果微信提现失败 则抛出异常 回滚数据
        if (!result) {
            throw new ServiceException(ResultCode.WALLET_ERROR_INSUFFICIENT);
        }
        return result;
    }


    @Override
    @Transactional
    public void audit(String applyId, Boolean result, String remark) {


        UserWithdrawalMessage userWithdrawalMessage = new UserWithdrawalMessage();
        //查询申请记录
        UserWithdrawApply userWithdrawApply = this.getById(applyId);

        //如果申请记录不存在或者申请记录状态不是申请中 则抛出异常
        if (userWithdrawApply == null || !userWithdrawApply.getApplyStatus().equals(WithdrawStatusEnum.APPLY.name())) {
            throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_APPLY_NOT_EXIST);
        }

        userWithdrawApply.setInspectRemark(remark);
        userWithdrawApply.setInspectTime(new Date());
        //获取账户余额
        WalletVO walletVO = walletService.getWalletVO(userWithdrawApply.getUserId());
        //校验金额是否满足提现，因为是从冻结金额扣减，所以校验的是冻结金额
        if (walletVO.getFrozenBalance() < userWithdrawApply.getApplyMoney()) {
            throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_FROZEN_AMOUNT_INSUFFICIENT);
        }
        //如果审核通过 则微信直接提现，反之则记录审核状态
        if (result) {
            userWithdrawApply.setApplyStatus(WithdrawStatusEnum.VIA_AUDITING.name());
            //提现，微信提现成功后扣减冻结金额
            Boolean bool = this.withdrawal(userWithdrawApply);
            if (Boolean.TRUE.equals(bool)) {
                userWithdrawalMessage.setStatus(WithdrawStatusEnum.VIA_AUDITING.name());
                //保存修改审核记录
                this.updateById(userWithdrawApply);

                walletService.reduceFrozen(UserWalletUpdateDTO.builder()
                        .userId(userWithdrawApply.getUserId())
                        .detail("提现申请：[" + applyId + "], 审核通过，余额提现")
                        .nickname(userWithdrawApply.getRealName())
                        .amount(userWithdrawApply.getApplyMoney())
                        .orderSn(applyId)
                        .serviceType(WalletServiceTypeEnum.WITHDRAWAL)
                        .outTradeNo(SnowFlake.getIdStr())
                        .platformWalletId(PlatformWalletEnum.OFFLINE.getWalletUserId())
                        .build());
            } else {
                //如果提现失败则无法审核
                throw new ServiceException(ResultCode.WALLET_APPLY_ERROR);
            }
        } else {
            userWithdrawalMessage.setStatus(WithdrawStatusEnum.FAIL_AUDITING.name());
            //如果审核拒绝 审核备注必填
            if (CharSequenceUtil.isEmpty(remark)) {
                throw new ServiceException(ResultCode.WALLET_REMARK_ERROR);
            }
            userWithdrawApply.setApplyStatus(WithdrawStatusEnum.FAIL_AUDITING.name());
            //保存修改审核记录
            this.updateById(userWithdrawApply);
            //需要从冻结金额扣减到余额
            walletService.increaseWithdrawal(
                    UserWalletUpdateDTO.builder()
                            .userId(userWithdrawApply.getUserId())
                            .detail("审核拒绝，提现金额解冻到余额")
                            .nickname(userWithdrawApply.getRealName())
                            .amount(userWithdrawApply.getApplyMoney())
                            .serviceType(WalletServiceTypeEnum.WITHDRAWAL).build());
        }
        //发送审核消息
        userWithdrawalMessage.setUserId(userWithdrawApply.getUserId());
        userWithdrawalMessage.setPrice(userWithdrawApply.getApplyMoney());
        userWithdrawalMessage.setDestination(WithdrawalModeEnum.WECHAT);


        amqpSender.send(
                AmqpMessage.builder()
                        .exchange(amqpExchangeProperties.getUser())
                        .routingKey(UserRoutingKey.USER_WITHDRAWAL)
                        .message(userWithdrawalMessage)
                        .build()
        );

    }


    @Override
    public Page<UserWithdrawApply> getWithdrawPage(PageVO pageVO, WithdrawApplyQueryVO withdrawApplyQueryVO) {
        //构建查询条件
        /*QueryWrapper<UserWithdrawApply> queryWrapper = new QueryWrapper<>();
        //会员名称
        queryWrapper.like(!CharSequenceUtil.isEmpty(withdrawApplyQueryVO.getUserName()), "user_name",
                withdrawApplyQueryVO.getUserName());
        //充值订单号
        queryWrapper.eq(!CharSequenceUtil.isEmpty(withdrawApplyQueryVO.getSn()), "sn", withdrawApplyQueryVO.getSn());
        //会员id
        queryWrapper.eq(!CharSequenceUtil.isEmpty(withdrawApplyQueryVO.getUserId()), "user_id",
                withdrawApplyQueryVO.getUserId());
        //已付款的充值订单
        queryWrapper.eq(!CharSequenceUtil.isEmpty(withdrawApplyQueryVO.getApplyStatus()), "apply_status",
                withdrawApplyQueryVO.getApplyStatus());
        //开始时间和结束时间
        if (!CharSequenceUtil.isEmpty(withdrawApplyQueryVO.getStartDate()) && !CharSequenceUtil.isEmpty(withdrawApplyQueryVO.getEndDate())) {
            Date start = cn.hutool.core.date.DateUtil.parse(withdrawApplyQueryVO.getStartDate());
            Date end = cn.hutool.core.date.DateUtil.parse(withdrawApplyQueryVO.getEndDate());
            queryWrapper.between("create_time", start, end);
        }
        queryWrapper.orderByDesc("create_time");*/
        //查询返回数据
        return this.baseMapper.getWithdrawPage(PageUtil.initPage(pageVO), withdrawApplyQueryVO);
    }
}